package com.carey.blog.libs.baidu;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringEscapeUtils;

import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;

public class BDHttpClient {
	private static final URLFetchService urlFetchService = URLFetchServiceFactory
			.getURLFetchService();
	private final HashMap<String, String> globalCookies = new HashMap<String, String>();
	private static final Logger LOGGER = Logger.getLogger(BDHttpClient.class
			.getName());

	private static final String LOGIN_URL = "https://passport.baidu.com/?login";
	private String CREATBLOG_URL;
	private String MODIFYCATEGORY_URL;
	private String COMMITBLOG_URL;

	private String username;
	private String password;
	private String charset;

	private ArrayList<String> categories;
	private String bdstoken;
	private boolean needCreateCategory;

	public BDHttpClient(String username, String password, String charset) {
		this.username = username;
		this.password = password;
		this.charset = charset;

		this.CREATBLOG_URL = "http://hi.baidu.com/" + username + "/creat/blog";
		this.MODIFYCATEGORY_URL = "http://hi.baidu.com/" + username
				+ "/modify/category/0";
		this.COMMITBLOG_URL = "http://hi.baidu.com/" + username + "/commit";

		this.needCreateCategory = true;
	}

	public void publishBlog(String title, String content, String category)
			throws Exception {
		// step 1, login baidu and get cookies
		String cookies = LoginBaidu();
		LOGGER.info("cookies:" + cookies);
		if (cookies == null) {
			throw new Exception("cookies == null");
		}

		// step 2, get Bdstoken and categories
		getBdstokenAndCategories(cookies);
		LOGGER.info("bdstoken:" + bdstoken);
		if (bdstoken == null) {
			throw new Exception("bdstoken == null");
		}

		for (String cg : categories) {
			LOGGER.info("category:" + cg);
			if (cg.equalsIgnoreCase(category)) {
				needCreateCategory = false;
			}
		}

		// step 3, publish article
		postBlog(cookies, title, content, category);
	}

	private String LoginBaidu() {
		HashMap<String, String> params = new HashMap<String, String>();
		params.put("username", username);
		params.put("password", password);
		params.put("pwd", "1");

		try {
			httpPost(null, LOGIN_URL, generateQueryString(params));
		} catch (Exception e) {
			e.printStackTrace();
		}

		return globalCookies.get("set-cookie");
	}

	private ArrayList<String> getCategoriesList(String res) {
		ArrayList<String> categories = new ArrayList<String>();

		int index = res.indexOf("id=\"spBlogCatName\"");
		if (index != -1) {
			int selectstart = res.indexOf(">", index);
			int selectend = res.indexOf("</select>", index);

			if (selectstart != -1 && selectend != -1) {
				res = res.substring(selectstart + ">".length(), selectend);

				int start = res.indexOf("<option");
				while (start != -1) {
					int end = res.indexOf("</option>", start);
					if (end != -1) {
						String value = getValueFromOption(res.substring(start,
								end + "</option>".length()));
						if (value != null) {
							categories.add(value);
						}
						start = res.indexOf("<option",
								end + "</option>".length());
					} else {
						break;
					}
				}
			}
		}

		return categories;
	}

	// <option value="默认分类">默认分类</option>
	private String getValueFromOption(String input) {
		int index = input.indexOf("value=");
		if (index != -1) {
			int start = index + "value=".length() + 1;
			int end = input.indexOf("\"", start);
			if (end != -1) {
				return input.substring(start, end);
			}
		}
		return null;
	}

	private void getBdstokenAndCategories(String cookies) {
		try {
			String res = httpGet(cookies, CREATBLOG_URL, null);
			this.categories = getCategoriesList(res);
			Pattern p = Pattern.compile("bdstoken=([0-9a-z]+)\\W");
			Matcher m = p.matcher(res);
			if (m.find()) {
				this.bdstoken = m.group(1);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private String createCategory(String cookies, String bdstoken,
			String category) throws Exception {
		HashMap<String, String> params = new HashMap<String, String>();

		// bdstoken
		params.put("bdstoken", bdstoken);

		// create category
		params.put("ct", "2");
		params.put("cm", "1");

		// Article category, such as "Android", "Google", "默认分类"
		params.put("spBlogCatName", category);
		params.put("spRefURL", MODIFYCATEGORY_URL);

		return httpPost(cookies, COMMITBLOG_URL, generateQueryString(params));
	}

	private String postBlog(String cookies, String title, String content,
			String category) throws Exception {
		HashMap<String, String> params = new HashMap<String, String>();

		// bdstoken
		params.put("bdstoken", bdstoken);

		// new blog
		params.put("ct", "1");
		params.put("cm", "1");

		// add a new article
		params.put("spBlogID", "");
		params.put("edithid", "");
		params.put("spBlogCatName_o", "");

		// Article title
		params.put("spBlogTitle", title);

		// Article content
		// unescape like &nbsp; &gt;
		params.put("spBlogText", StringEscapeUtils.unescapeHtml(content));

		// Article category, such as "Android", "Google", "默认分类"
		params.put("spBlogCatName", category);

		// Article view Permissions,
		// 0 --> every one 1 --> only friend 3 --> only oneself
		params.put("spBlogPower", "0");

		// 0: forbidden comment 1: allow comment
		params.put("spIsCmtAllow", "1");

		// 0: allow share 1: forbidden share
		params.put("spShareNotAllow", "0");

		// verify code
		params.put("spVcode", "");
		params.put("spVerifyKey", "");

		if (needCreateCategory) {
			LOGGER.info("create category: <" + category + ">");
			// first create category
			createCategory(cookies, bdstoken, category);
		}

		return httpPost(cookies, COMMITBLOG_URL, generateQueryString(params));
	}

	private static String generateQueryString(HashMap<String, String> params) {
		StringBuffer sb = new StringBuffer();

		if (params != null && !params.isEmpty()) {
			Set<String> keys = params.keySet();

			for (String key : keys) {
				sb.append(key);
				sb.append("=");
				sb.append(params.get(key));
				sb.append("&");
			}

			// remove last &
			sb.deleteCharAt(sb.length() - 1);
		}

		return sb.toString();
	}

	private String httpGet(String cookies, String url, String queryString)
			throws Exception {
		String ret = null;

		if (queryString != null && !queryString.equals("")) {
			url += "?" + queryString;
		}

		final HTTPRequest request = new HTTPRequest(new URL(url));
		if (cookies != null) {
			request.setHeader(new HTTPHeader("Cookie", cookies));
		}

		try {
			final HTTPResponse response = urlFetchService.fetch(request);
			if (HttpServletResponse.SC_OK != response.getResponseCode()) {
				System.err.println("HttpGet Method failed: "
						+ response.getResponseCode());
			}
			ret = new String(response.getContent(), charset);
		} catch (Exception e) {
			throw new Exception(e);
		}

		return ret;
	}

	private String httpPost(String cookies, String url, String queryString)
			throws Exception {
		String ret = null;

		final HTTPRequest request = new HTTPRequest(new URL(url),
				HTTPMethod.POST);
		request.setHeader(new HTTPHeader("Content-Type",
				"application/x-www-form-urlencoded"));
		if (cookies != null) {
			request.setHeader(new HTTPHeader("Cookie", cookies));
		}

		if (queryString != null && !queryString.equals("")) {
			request.setPayload(queryString.getBytes(charset));
		}

		try {
			final HTTPResponse response = urlFetchService.fetch(request);
			if (HttpServletResponse.SC_OK != response.getResponseCode()) {
				System.err.println("HttpPost Method failed: "
						+ response.getResponseCode());
			}

			// Get site cookie and charset
			for (HTTPHeader header : response.getHeaders()) {
				if (header.getName().equalsIgnoreCase("set-cookie")) {
					globalCookies.put("set-cookie", header.getValue());
					break;
				}
			}
			ret = new String(response.getContent(), charset);
		} catch (Exception e) {
			throw new Exception(e);
		}

		return ret;
	}
}
